package com.atguigu.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.to.OrderTo;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.ware.entity.SkuWareHasStock;
import com.atguigu.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimall.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimall.ware.exception.NoStockException;
import com.atguigu.gulimall.ware.feign.OrderFeignService;
import com.atguigu.gulimall.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimall.ware.service.WareOrderTaskService;
import com.atguigu.gulimall.ware.to.StockDetailTo;
import com.atguigu.gulimall.ware.to.StockLockedTo;
import com.atguigu.gulimall.ware.vo.OrderItemVo;
import com.atguigu.gulimall.ware.vo.OrderLockVo;
import com.atguigu.gulimall.ware.vo.OrderVo;
import com.atguigu.gulimall.ware.vo.SkuStockVo;
import com.mysql.cj.util.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;
import com.atguigu.gulimall.ware.dao.WareSkuDao;
import com.atguigu.gulimall.ware.entity.WareSkuEntity;
import com.atguigu.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    private WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    private OrderFeignService orderFeignService;

    @Autowired
    private WareSkuDao wareSkuDao;

    @Autowired
    private WareOrderTaskService wareOrderTaskService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /***
     * 释放库存
     */
    // 防止订单服务卡顿,导致订单状态一直改不了,库存消息优先到期,查看订单状态为新建状态(因为只有当订单状态为已取消的状态才会解锁库存),什么都不做就走了
    // 导致卡顿的订单,永远不能解锁库存
    @Transactional
    @Override
    public void releaseWare(OrderTo order) {
        String orderSn = order.getOrderSn();
        // 查询最新库存的状态,防止重复解锁库存
        WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskService.getOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", orderSn));
        List<WareOrderTaskDetailEntity> detailEntityList = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>()
                .eq("task_id", wareOrderTaskEntity.getId())
                .eq("lock_status", 1));
        // 解锁操作
        for (WareOrderTaskDetailEntity wareOrderTaskDetailEntity : detailEntityList) {
            releaseStock(wareOrderTaskDetailEntity.getSkuId()
                    ,wareOrderTaskDetailEntity.getWareId()
                    ,wareOrderTaskDetailEntity.getSkuNum()
                    ,wareOrderTaskDetailEntity.getId());
        }
    }

    /***
     * 解锁场景：
     * 1.下单成功，库存锁定成功，接下来的业务调用失败导致订单回滚。之前锁定的库存就要自动解锁。
     * 2.锁库存失败无需解锁
     * 解决方案：通过查询订单的锁库存信息，如果有则仅仅说明库存锁定成功，还需判断是否有订单信息，如果有订单信息则判断订单状态，若订单状态已取消则解锁库存，
     * 反之：不能解锁库存，如果没有订单信息则需要解锁库存，如果没有锁库存信息则无需任何操作。
     */
    // 解锁库存服务
    @Override
    public void unlockStock(StockLockedTo stockLockedTo) {
        StockDetailTo detail = stockLockedTo.getDetail();
        Long detailId = detail.getId();
        // 查询库存工作单的信息 有:解锁库存 没有:库存锁定失败,数据自定义回滚无需解锁
        WareOrderTaskDetailEntity detailEntity = wareOrderTaskDetailService.getById(detailId);
        if(null!=detailEntity) {
            // 有:解锁库存
            // 库存工作单id
            Long id = stockLockedTo.getId();
            WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskService.getById(id);
            String orderSn = wareOrderTaskEntity.getOrderSn();
            // 远程服务调用,获取订单状态信息
            // 先判断订单是否存在
            R r = orderFeignService.getOrderStatus(orderSn);
            if(r.getCode().equals(0)){
                OrderVo orderVo = r.getData(new TypeReference<OrderVo>(){});
                // 订单不存在或者订单被关闭,都需要去解锁库存
                if(null == orderVo || orderVo.getStatus().equals(4)){
                    // 当且仅当 锁定状态为 已锁定时才去解锁
                    if(detailEntity.getLockStatus().equals(1)){
                        releaseStock(detailEntity.getSkuId(),detailEntity.getWareId(),detail.getSkuNum(),detailEntity.getId());
                    }
                }
            }else{
                // 远程服务调用失败,抛出异常,将消息放回消息队列中
                throw new RuntimeException("远程调用订单服务失败!!!");
            }
        } else {
            // 订单的库存详情信息不存在,无需解锁
        }
    }

    // 库存解锁方法
    private void releaseStock(Long skuId,Long wareId,Integer skuNum,Long taskDetailId){
        // 解锁库存
        wareSkuDao.unLockStock(skuId,wareId,skuNum);
        // 更新库存工作单状态
        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
        wareOrderTaskDetailEntity.setId(taskDetailId);
        wareOrderTaskDetailEntity.setLockStatus(2); // 已解锁
        wareOrderTaskDetailService.updateById(wareOrderTaskDetailEntity);
    }

    @Transactional(rollbackFor = NoStockException.class)
    @Override
    public Boolean lockOrderStock(OrderLockVo orderLockVo) {
        System.out.println("方法执行到这里了lockOrderStock");
        // TODO 保存库存工作单的详情,方便回溯
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(orderLockVo.getOrderSn());
        wareOrderTaskService.save(wareOrderTaskEntity);

        // 1.找到每个商品在哪些仓库有库存
        List<OrderItemVo> locks = orderLockVo.getLocks();
        List<SkuWareHasStock> collect = locks.stream().map(item -> {
            SkuWareHasStock skuWareHasStock = new SkuWareHasStock();
            skuWareHasStock.setSkuId(item.getSkuId());
            skuWareHasStock.setNum(item.getCount());
            // 查询商品在哪些仓库有库存
            // SELECT ware_id from wms_ware_sku WHERE sku_id = #{skuId} AND stock - stock_locked > 0;
            List<Long> wareIds = wareSkuDao.listWareIds(item.getSkuId());
            skuWareHasStock.setWareIds(wareIds);
            return skuWareHasStock;
        }).collect(Collectors.toList());
        // 扣库存
        for (SkuWareHasStock wareHasStock : collect) {
            // 标识位
            Boolean flag = false;
            Integer num = wareHasStock.getNum();
            Long skuId = wareHasStock.getSkuId();
            List<Long> wareIds = wareHasStock.getWareIds();

            if(wareIds == null || wareIds.size()==0){
                throw new NoStockException(skuId);
            }

            for (Long wareId : wareIds) {
                // count = 1 则表示锁库存成功
                Long count = wareSkuDao.lockSkuStock(skuId,wareId,num);
                // 比如有两个仓库,1号仓库和2号仓库,假如1号仓库不够,2号仓库够,
                // 在一个sku多个仓库的情况下只要有一个仓库能满足锁定库存就行,并不是说只要有一个仓库不满足就报错
                if (count == 1){
                   flag = true;
                   // TODO 告诉MQ库存锁定成功
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity(null, skuId, null, num, wareOrderTaskEntity.getId(), wareId,1);
                    wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);

                    StockLockedTo stockLockedTo = new StockLockedTo();
                    stockLockedTo.setId(wareOrderTaskEntity.getId());
                    StockDetailTo stockDetailTo = new StockDetailTo();
                    BeanUtils.copyProperties(wareOrderTaskDetailEntity,stockDetailTo);
                    stockLockedTo.setDetail(stockDetailTo);
                    rabbitTemplate.convertAndSend("stock.event.exchange","stock.locked",stockLockedTo);
                    break;
                }
            }
            if (flag==false){
                throw new NoStockException(skuId);
            }
        }
        // 执行到这里没有抛出异常,则所有商品锁库存成功
        return true;
    }

    @Override
    public List<SkuStockVo> queryStock(List<Long> skuIds) {

        List<SkuStockVo> skuStockVos = skuIds.stream().map(skuId -> {
            SkuStockVo skuStockVo = new SkuStockVo();
            Integer stock = this.baseMapper.queryStock(skuId);
            skuStockVo.setSkuId(skuId);
            skuStockVo.setHasStock(stock == null ? false:stock > 0);
            return skuStockVo;
        }).collect(Collectors.toList());

        return skuStockVos;

    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {

        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();

        String wareId = (String)params.get("wareId");
        if(!StringUtils.isNullOrEmpty(wareId)){
            wrapper.eq("ware_id",wareId);
        }

        String skuId = (String)params.get("skuId");
        if(!StringUtils.isNullOrEmpty(skuId)){
            wrapper.eq("sku_id",skuId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void addStock(String skuName, Long skuId, Long wareId, Integer skuNum) {

        WareSkuEntity wareSkuEntity = this.baseMapper.selectOne(
                new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));

        if(wareSkuEntity == null){
            //新增
            wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setStock(skuNum);
        }else{
            wareSkuEntity.setStock(wareSkuEntity.getStock() + skuNum);
        }

        wareSkuEntity.setSkuName(skuName);
        wareSkuEntity.setWareId(wareId);
        wareSkuEntity.setSkuId(skuId);

        this.saveOrUpdate(wareSkuEntity);

    }

}